#include <cassert>

#include <algorithm>

#include "Topology.h"

Topology::Topology() : mDuringTriangleConstruction(false), mCurrentVertexIndex(-1), mDuringHalfEdgeConstruction(false)
{
}

Topology::~Topology()
{
}

void Topology::beginTriangles(int triangleCount)
{
	mVertexIndicesPolygonIndices.reserve(triangleCount * 3);
	mDuringTriangleConstruction = true;
}

void Topology::triangle(int triangleIndex, int firstVertexIndex, int secondVertexIndex, int thirdVertexIndex)
{
	if (mDuringTriangleConstruction)
	{
		QWord vertexIndexPolygonIndex;

		vertexIndexPolygonIndex.lowFourBytes = firstVertexIndex;
		vertexIndexPolygonIndex.highFourBytes = triangleIndex;
		mVertexIndicesPolygonIndices.push_back(vertexIndexPolygonIndex);
		
		vertexIndexPolygonIndex.lowFourBytes = secondVertexIndex;
		vertexIndexPolygonIndex.highFourBytes = triangleIndex;
		mVertexIndicesPolygonIndices.push_back(vertexIndexPolygonIndex);
		
		vertexIndexPolygonIndex.lowFourBytes = thirdVertexIndex;
		vertexIndexPolygonIndex.highFourBytes = triangleIndex;
		mVertexIndicesPolygonIndices.push_back(vertexIndexPolygonIndex);
	}
}

void Topology::endTriangles()
{
	// Get vertex index -> polygon index.
	std::sort(mVertexIndicesPolygonIndices.begin(), mVertexIndicesPolygonIndices.end(), lessLowFourBytes());

	mDuringTriangleConstruction = false;
}

void Topology::removeTriangles()
{
	mVertexIndicesPolygonIndices.clear();
}

void Topology::enterRings()
{
	mCurrentIteratorForRing = mVertexIndicesPolygonIndices.begin();
	mCurrentVertexIndex = 0;
}

int Topology::getPolygonCountInRing()
{
	equalLowFourBytes equal(mCurrentVertexIndex);

	mCurrentIteratorForRing = std::find_if(mCurrentIteratorForRing, mVertexIndicesPolygonIndices.end(), equal);
	assert(mCurrentIteratorForRing != mVertexIndicesPolygonIndices.end());

	mCurrentCountForRing = std::count_if(mCurrentIteratorForRing, mVertexIndicesPolygonIndices.end(), equal);

	++ mCurrentVertexIndex;

	return mCurrentCountForRing;
}

int Topology::getPolygonIndexInRing(int offset)
{
	return (*(mCurrentIteratorForRing + offset)).highFourBytes;
}

void Topology::leaveRings()
{
	mCurrentIteratorForRing = mVertexIndicesPolygonIndices.end();
	mCurrentCountForRing = 0;
	mCurrentVertexIndex = -1;
}


void Topology::beginHalfEdges(int triangleCount)
{
	mDuringHalfEdgeConstruction = true;
}

void Topology::halfEdge(int triangleIndex, int firstVertexIndex, int secondVertexIndex, int thirdVertexIndex)
{
	QWord halfEdge;

	halfEdge.lowFourBytes = firstVertexIndex;
	halfEdge.highFourBytes = secondVertexIndex;
	mHalfEdges.insert(std::make_pair(halfEdge, triangleIndex));

	halfEdge.lowFourBytes = secondVertexIndex;
	halfEdge.highFourBytes = thirdVertexIndex;
	mHalfEdges.insert(std::make_pair(halfEdge, triangleIndex));

	halfEdge.lowFourBytes = thirdVertexIndex;
	halfEdge.highFourBytes = firstVertexIndex;
	mHalfEdges.insert(std::make_pair(halfEdge, triangleIndex));
}

void Topology::endHalfEdges()
{
	mDuringHalfEdgeConstruction = false;
}

void Topology::removeHalfEdges()
{
	mHalfEdges.clear();
}

void Topology::enterConnection(int a, int b)
{
	mTargetEdge.lowFourBytes = a;
	mTargetEdge.highFourBytes = b;
}

bool Topology::atBoundary()
{
	assert(mTargetEdge.lowFourBytes != -1 && mTargetEdge.highFourBytes != -1);
	
	std::map<QWord, int, lessEightBytes>::iterator aToB = mHalfEdges.find(mTargetEdge);

	mTargetEdge.swap();
	std::map<QWord, int, lessEightBytes>::iterator bToA = mHalfEdges.find(mTargetEdge);

	if (aToB != mHalfEdges.end() && bToA != mHalfEdges.end())
	{
		mTwoPolygons.lowFourBytes = aToB->second;
		mTwoPolygons.highFourBytes = bToA->second;

		return false;
	}
	else if(aToB != mHalfEdges.end())
	{
		mTwoPolygons.lowFourBytes = aToB->second;
		mTwoPolygons.highFourBytes = -1;
	}
	else if(bToA != mHalfEdges.end())
	{
		mTwoPolygons.lowFourBytes = bToA->second;
		mTwoPolygons.highFourBytes = -1;
	}

	return true;
}

int Topology::getFirstPolygonIndexSharedHalfEdge()
{
	return mTwoPolygons.lowFourBytes;
}

int Topology::getSecondPolygonIndexSharedHalfEdge()
{
	return mTwoPolygons.highFourBytes;
}

void Topology::leaveConnection()
{
	mTargetEdge.lowFourBytes = -1;
	mTargetEdge.highFourBytes = -1;
}
